package cate.game.role.farm.udgpalace;

import cate.common.table.d.GD;
import cate.common.table.d.GDFight;
import cate.common.table.d.GDUdgPalace;
import cate.common.table.farm.udgpalace.row.*;
import cate.common.util.GameResult;
import cate.game.attr.FightAttr;
import cate.game.play.fighter.HeroFighter;
import cate.game.play.param.FightParam;
import cate.game.play.param.HpExtend;
import cate.game.play.part.BattleHero;
import cate.game.play.part.FightSide;
import cate.game.play.part.FightTeam;
import cate.game.play.part.PlaySide;
import cate.game.play.proce.play.PlayPO;
import cate.game.res.MixRes;
import cate.game.res.MixResAdder;
import cate.game.res.MixResItem;
import cate.game.role.Role;
import cate.game.role.RoleDependent;
import cate.game.role.RoleSnapshot;
import cate.game.role.bag.hero.Hero;
import cate.game.role.farm.udgpalace.msg.UdgPalaceHandleEventResp;
import com.fasterxml.jackson.annotation.JsonIgnore;
import easy.java.dev.note.NoteClass;
import easy.java.dev.note.NoteField;
import easy.java.practice.errorcode.EcResult;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@NoteClass("当前层数据")
public class UdgPalaceLevel extends RoleDependent {
    @NoteField("当前层数")
    public int level;
    @NoteField("最高层数")
    public int maxLevel;
    @NoteField("boss血量继承")
    public Map<Integer, Double> bossHpExtends;
    @NoteField("事件 <位置（1~25）, 事件>")
    public Map<Integer, UdgPalaceEvent> events;

    @Override
    public void initialize(Role role) {
        super.initialize(role);
        if (bossHpExtends == null) {
            bossHpExtends = new HashMap<>();
        }
        if (events == null) {
            events = new HashMap<>();
        }
        events.values().forEach(UdgPalaceEvent::initialize);
    }

    private boolean allGuardDead(int guardNum) {
        return bossHpExtends.entrySet().stream().filter(e -> e.getValue() <= 0).count() >= guardNum;
    }

    private boolean guardDead(int bossId) {
        return bossHpExtends != null && bossHpExtends.containsKey(bossId) && bossHpExtends.get(bossId) <= 0;
    }

    public GameResult<Void> vsBoss(int bossId, Hero hero, HpExtend heroHpExtend, boolean skip, boolean autoHandleEvent) {
        GameResult<Void> r = new GameResult<>();
        UdgPalaceLevelRow levelRow = getLevelRow();
        if (levelRow == null) {
            return r.fail("错误的关卡");
        }
        if (bossId < 0 || (!levelRow.guardIds.contains(bossId) && bossId != levelRow.bossId)) {
            return r.fail("请选择正确的boss");
        }
        if (bossId == levelRow.bossId && !allGuardDead(levelRow.guardIds.size())) {
            return r.fail("击杀全部守卫后才可挑战boss");
        }
        UdgPalaceGuardRow bossRow = role.getGame().table.udgPalace.guard.get(bossId);
        if (bossRow == null) {
            return r.fail("boss配置异常");
        }
        if (guardDead(bossId)) {
            return r.fail("守卫已经死亡");
        }
        EcResult<FightSide> bossFsRes = role.getGame().fight.side.createFightSideByCode(GDFight.Team.B, bossRow.guardConfig);
        if (!bossFsRes.ok()) {
            return r.fail(bossFsRes);
        }
        FightSide fightSide = createFightSideByHero(hero);
        UdgPalaceFTC ftc = new UdgPalaceFTC(role, bossId, autoHandleEvent);
        ftc.playStory.level = level;
        ftc.param = buildBuffFightParam(fightSide);
        ftc.param.sideA.hpExtend.read(heroHpExtend);
        if (bossHpExtends.containsKey(bossId)) {
            ftc.param.sideB.hpExtend.read(buildBossHpExtend(bossId, bossFsRes.data));
        }
        ftc.skip = skip;
        EcResult<PlayPO> fightRes = role.getGame().play.startByRole(role, ftc, fightSide, bossFsRes.data);
        if (!fightRes.ok()) {
            return r.fail(fightRes);
        }
        return r;
    }

    private FightParam buildBuffFightParam(FightSide fightSide) {
        FightParam param = new FightParam();
        HeroFighter heroFighter = fightSide.ft.getAllHero().get(0);
        if (role.getFarm().udgPalace.team.containsWishBuff(heroFighter.uid)) {
            UdgPalaceItemRow itemRow = role.getGame().table.udgPalace.item.get(GDUdgPalace.ItemType.WISH.getItemId());
            if (itemRow != null) {
                param.sideA.attrAppend.addHeroAttr(heroFighter.heroTid, new FightAttr(itemRow.param));
            }
            role.getFarm().udgPalace.team.buffInvalid(heroFighter.uid);
        }
        return param;
    }

    public HpExtend buildBossHpExtend(int bossId, FightSide fs) {
        HpExtend hpExtend = new HpExtend();
        hpExtend.recordByHeroTid(fs.ps.hs.get(0).hero.tid, bossHpExtends.get(bossId));
        return hpExtend;
    }

    private FightSide createFightSideByHero(Hero hero) {
        PlaySide ps = new PlaySide();
        ps.snapshot = new RoleSnapshot(role);
        BattleHero bh = role.getGame().fight.hero.buildInBag(role, hero);
        if (bh != null) {
            bh.pos = (byte) 1;
            ps.addHero(bh);
        }
        ps.updatePower();
        return role.getGame().fight.side.createFightSideByPlaySide(GDFight.Team.A, ps);
    }

    @JsonIgnore
    private UdgPalaceLevelRow getLevelRow() {
        return role.getGame().table.udgPalace.level.get(level);
    }

    @JsonIgnore
    private UdgPalaceLevelRow getNextLevelRow() {
        return role.getGame().table.udgPalace.level.get(level + 1);
    }

    public GameResult<Integer> killAllBoss() {
        GameResult<Integer> r = new GameResult<>();
        UdgPalaceLevelRow levelRow = getLevelRow();
        if (levelRow == null) {
            return r.fail("错误的关卡");
        }
        int killed = 0;
        for (int guardId : levelRow.guardIds) {
            killed += killAndRecordBoss(guardId).ok() ? 1 : 0;
        }
        killed += killAndRecordBoss(levelRow.bossId).ok() ? 1 : 0;
        if (killed == 0) {
            return r.fail("守卫均已死亡");
        }
        r.data = killed;
        return r;
    }

    public GameResult<Void> killBoss(int bossId) {
        GameResult<Void> r = new GameResult<>();
        UdgPalaceLevelRow levelRow = getLevelRow();
        if (levelRow == null) {
            return r.fail("错误的关卡");
        }
        if (bossId < 0 || (!levelRow.guardIds.contains(bossId) && bossId != levelRow.bossId)) {
            return r.fail("请选择正确的boss");
        }
        if (bossId == levelRow.bossId && !allGuardDead(levelRow.guardIds.size())) {
            return r.fail("击杀全部守卫后才可挑战boss");
        }
        r = killAndRecordBoss(bossId);
        if (!r.ok()) {
            return r;
        }
        winChallenge(bossId);
        return r;
    }

    private GameResult<Void> killAndRecordBoss(int bossId) {
        GameResult<Void> r = new GameResult<>();
        PlaySide guardPs = getGuardPs(bossId);
        if (guardPs == null) {
            return r.fail("请选择正确的守卫");
        }
        if (guardDead(bossId)) {
            return r.fail("守卫已经死亡");
        }
        bossHpExtends.put(bossId, 0D);
        winChallenge(bossId);
        return r;
    }

    private PlaySide getGuardPs(int bossId) {
        UdgPalaceGuardRow bossRow = role.getGame().table.udgPalace.guard.get(bossId);
        if (bossRow == null) {
            return null;
        }
        EcResult<PlaySide> guardPsRes = role.getGame().fight.side.createPlaySideByCode(bossRow.guardConfig);
        if (!guardPsRes.ok()) {
            return null;
        }
        return guardPsRes.data;
    }

    public void afterChallenge(int bossId, boolean win, FightTeam ft) {
        ft.getAllHero().forEach(f -> bossHpExtends.put(bossId, f.attr.total().hpCurRate() / GD.W_10000_0));
        if (win) {
            winChallenge(bossId);
        }
    }

    private void winChallenge(int bossId) {
        UdgPalaceLevelRow levelRow = getLevelRow();
        if (levelRow != null) {
            int bossIdx = levelRow.bossId == bossId ? 5 : levelRow.guardIds.indexOf(bossId);
            UdgPalaceGuardRow guardRow = role.getGame().table.udgPalace.guard.get(bossId);
            if (guardRow != null) {
                if (bossIdx < 5) {
                    for (int pos = guardRow.pos - 5; pos <= guardRow.pos; ++pos) {
                        createNewEvent(pos, guardRow.killEvent);
                    }
                } else {
                    createNewEvent(guardRow.pos, guardRow.killEvent);
                }
            }
        }
    }

    private void createNewEvent(int pos, int killEvent) {
        UdgPalaceEventRow eventRow = role.getGame().table.udgPalace.event.get(killEvent);
        if (eventRow == null) {
            return;
        }
        Map<Integer, Integer> eventTypes = new HashMap<>();
        events.values().forEach(e -> eventTypes.put(e.type, eventTypes.getOrDefault(e.type,0) + 1));
        List<Integer> excludes = eventTypes.keySet().stream()
                .filter(udgPalaceEvent -> role.getGame().table.udgPalace.eventType.get(udgPalaceEvent).unique)
                .collect(Collectors.toList());
        GDUdgPalace.EventType newEvent = role.getGame().table.udgPalace.event.randomEvent(killEvent, excludes);
        UdgPalaceEvent event = new UdgPalaceEvent(newEvent.getType());
        switch (newEvent) {
            case DROP:
                UdgPalaceEventRow.ItemGroupList.Item dropItem = eventRow.dropGroup.get();
                event.reward = new MixRes().addItem(new MixResItem(dropItem.type, dropItem.tid, dropItem.num));
                break;
            case BOX:
                UdgPalaceEventRow.ItemGroupList.Item boxItem = eventRow.boxGroup.get();
                event.reward = new MixRes().addItem(new MixResItem(boxItem.type, boxItem.tid, boxItem.num));
                break;
            case SHOP:
                event.group = eventRow.shopGroup.get().getGroupId().get(0);
                break;
            case QUESTION:
                event.group = eventRow.questionGroup.get().getGroupId().get(0);
                event.extraGroup = role.getGame().table.udgPalace.question.randomQuestionGroup(newEvent.getType(), event.group);
                break;
            case MYSTERIOUS:
                event.group = eventRow.mysteriousGroup.get().getGroupId().get(0);
                event.extraGroup = role.getGame().table.udgPalace.question.randomQuestionGroup(newEvent.getType(), event.group);
                break;
            case TURNTABLE:
                event.group = eventRow.turntableGroup.get().getGroupId().get(0);
                break;
            default:
                break;
        }
        events.put(pos, event);
    }

    public void autoHandleEvent() {
        for (Map.Entry<Integer, UdgPalaceEvent> eventEntry : events.entrySet()) {
            GDUdgPalace.EventType eventType = GDUdgPalace.EventType.transEventType(eventEntry.getValue().type);
            switch (eventType) {
                case QUESTION:
                case MYSTERIOUS:
                case TURNTABLE:
                case GUESS:
                case BOX:
                    handlePosEvent(eventEntry.getKey(), 0,false);
                    break;
                default:
                    break;
            }
        }
    }

    public GameResult<Void> handlePosEvent(int pos, int chooseId, boolean dialog) {
        GameResult<Void> r = new GameResult<>();
        UdgPalaceEvent event = events.get(pos);
        if (event == null) {
            return r.fail("该位置没有事件发生");
        }
        if (event.done) {
            if (event.type >= 8) {
                return r.fail("该事件已完成");
            }
            return r.fail("");
        }
        int resOption = 0;
        role.log.setOperationSub(event.type);
        GDUdgPalace.EventType eventType = GDUdgPalace.EventType.transEventType(event.type);
        switch (eventType) {
            case NONE:
                break;
            case DROP:
                role.getFarm().udgPalace.team.recordReward(event.reward);
            case BOX:
                new MixResAdder()
                        .setRes(event.reward)
                        .setSendMailWhenFull(true)
                        .setDialogReward(dialog && eventType == GDUdgPalace.EventType.BOX)
                        .exe(role);
                break;
            case RECOVER:
            case KILL:
            case WISH:
            case DAMAGE:
                role.getFarm().udgPalace.team.updateItems(eventType, event.group);
                break;
            case SHOP:
                role.getFarm().udgPalace.team.addMallItems(event.group);
                break;
            case QUESTION:
            case MYSTERIOUS:
                UdgPalaceQuestionRow questionRow = role.getGame().table.udgPalace.question.getQuestionRow(
                        eventType.getType(),
                        event.group,
                        event.extraGroup,
                        chooseId);
                if (questionRow == null) {
                    return r.fail("错误的选项");
                }
                if (StringUtils.isNotBlank(questionRow.consumeStr)) {
                    GameResult<MixResItem> consumeRes = new MixRes(questionRow.consumeStr).consume(role);
                    if (!consumeRes.ok()) {
                        return r.fail(consumeRes);
                    }
                }
                new MixResAdder()
                        .setRes(questionRow.rewardStr)
                        .setSendMailWhenFull(true)
                        .setDialogReward(dialog)
                        .exe(role);
                if (eventType == GDUdgPalace.EventType.QUESTION) {
                    if (questionRow.correct) {
                        resOption = questionRow.id;
                    } else {
                        UdgPalaceQuestionRow correntRow = role.getGame().table.udgPalace.question.getCorrectRow(
                                1,
                                event.group,
                                event.extraGroup);
                        if (correntRow != null) {
                            resOption = correntRow.id;
                        }
                    }
                }
                break;
            case TURNTABLE:
                UdgPalaceTurntableRow row = role.getGame().table.udgPalace.turntable.randomRow(event.group);
                if (row == null) {
                    return r.fail("转盘数据异常");
                }
                new MixResAdder()
                        .setRes(row.rewardStr)
                        .setSendMailWhenFull(true)
                        .exe(role);
                resOption = row.id;
                break;
            case GUESS:
                GameResult<MixResItem> consumeRes = new MixRes(GDUdgPalace.GUESS_CONSUME_STR).consume(role);
                if (!consumeRes.ok()) {
                    return r.fail(consumeRes);
                }
                UdgPalaceGuessRow guessRow = role.getGame().table.udgPalace.guess.getRow(chooseId);
                if (guessRow != null) {
                    new MixResAdder()
                            .setRes(guessRow.rewardStr)
                            .setSendMailWhenFull(true)
                            .exe(role);
                    resOption = guessRow.id;
                }
                break;
            case NEXT:
                role.getFarm().udgPalace.special = false;
                UdgPalaceLevelRow nextRow = getNextLevelRow();
                if (nextRow == null) {
                    return r.fail("您已通关");
                }
                upgrade();
                break;
            default:
                break;
        }
        event.done = true;
        if (canGoToNext()) {
            createNextFloorEvent();
        }
        if (dialog) {
            role.getFarm().udgPalace.noticeUpdate();
            role.sendNow(new UdgPalaceHandleEventResp(pos, chooseId, event.type, resOption));
        }
        return r;
    }

    private void createNextFloorEvent() {
        UdgPalaceEvent last = getLastEvent();
        last.choose = 0;
        last.group = 0;
        last.extraGroup = 0;
        last.reward = null;
        last.done = false;
        last.type = GDUdgPalace.EventType.NEXT.getType();
    }

    private boolean canGoToNext() {
        UdgPalaceEvent lastEvent = getLastEvent();
        return lastEvent != null && lastEvent.type != GDUdgPalace.EventType.NEXT.getType() && lastEvent.done;
    }

    @JsonIgnore
    private UdgPalaceEvent getLastEvent() {
        return events.get(GDUdgPalace.LAST_POS);
    }

    public void upgrade() {
        level += 1;
        if (level > maxLevel) {
            maxLevel = level;
            role.getGame().rank.udgPalace.record(role, level);
        }
        events.clear();
        bossHpExtends.clear();
    }

    public void reset() {
        if (level == 0) {
            level = 1;
            maxLevel = 1;
        } else {
            level = Math.max(maxLevel - GDUdgPalace.RESET_LEVEL_NUM, 1);
        }
        events.clear();
        bossHpExtends.clear();
    }
}
